package com.zhz.mall.common.util.enums;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.zhz.mall.common.util.FunctionUtils;
import com.zhz.mall.common.util.cache.CacheUtils;
import com.zhz.mall.common.exception.BusinessException;
import org.springframework.lang.Nullable;

import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

/**
 * EnumUtils
 * <p>
 * 枚举工具类
 *
 * @see EnumValueResolver
 */
public class EnumUtils {


    /**
     * 根据 value 查询枚举（将 value 转为字符串再进行匹配）
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E getByStringValue(Class<E> enumType, Object value) {
        if (isNullValue(value)) {
            return null;
        }

        String valueStr = value.toString();
        E[] values = enumType.getEnumConstants();
        for (E e : values) {
            if (e.getStringValue().equals(valueStr)) {
                return e;
            }
        }
        return null;
    }


    /**
     * 根据 value 查询枚举
     */
    @Nullable
    public static <E extends Enum<E>> E getByValue(Class<E> enumType, Object value, Function<E, ?> function) {
        return getByValue(enumType, value, function, null);
    }


    /**
     * 根据 value 查询枚举
     *
     * @param enumType     枚举类型
     * @param value        解析值
     * @param function     解析函数
     * @param defaultValue 默认值
     */
    @Nullable
    public static <E extends Enum<E>> E getByValue(Class<E> enumType, Object value, Function<E, ?> function, @Nullable E defaultValue) {
        if (isNullValue(value)) {
            return defaultValue;
        }

        E[] values = enumType.getEnumConstants();
        for (E e : values) {
            if (function.apply(e).equals(value)) {
                return e;
            }
        }
        return defaultValue;
    }


    /**
     * 根据 value 查询枚举
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E getByValue(Class<E> enumType, Object value) {
        return getByValue(enumType, value, (E) null);
    }


    /**
     * 根据 value 查询枚举
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E getByValue(Class<E> enumType, Object value, @Nullable E defaultValue) {
        if (isNullValue(value)) {
            return defaultValue;
        }

        E[] values = enumType.getEnumConstants();
        for (E e : values) {
            if (e.getValue().equals(value)) {
                return e;
            }
        }
        return defaultValue;
    }


    /**
     * 根据 value 查询枚举
     */
    public static <E extends Enum<E>> E requiredByValue(Class<E> enumType, Object value, Function<E, ?> function) {
        E e = getByValue(enumType, value, function);
        if (Objects.isNull(e)) {
            throw new IllegalStateException(StrUtil.format("不支持的枚举[{}]值: {}", enumType.getSimpleName(), value));
        }
        return e;
    }


    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E requiredByValue(Class<E> enumType, Object value) {
        E e = getByValue(enumType, value);
        if (Objects.isNull(e)) {
            throw new BusinessException("不支持的枚举值: " + value);
        }
        return e;
    }


    /**
     * 根据 value 查询枚举（并且必须是候选值之一）
     */
    @SafeVarargs
    public static <E extends Enum<E>> E requiredByValue(Class<E> enumType, Object value, Function<E, ?> function, E... candidates) {
        E e = requiredByValue(enumType, value, function);
        return candidate(enumType, e, value, candidates);
    }


    /**
     * 根据 value 查询枚举（并且必须是候选值之一）
     */
    @SafeVarargs
    public static <E extends Enum<E> & EnumValueResolver<E, ?>> E requiredByValue(Class<E> enumType, Object value, E... candidates) {
        E e = requiredByValue(enumType, value);
        return candidate(enumType, e, value, candidates);
    }


    /**
     * 判断是否为空值
     */
    private static boolean isNullValue(Object value) {
        if (Objects.isNull(value)) {
            return true;
        }
        return value instanceof String && StrUtil.isBlank(value.toString());
    }


    @SafeVarargs
    private static <E extends Enum<E>> E candidate(Class<E> enumType, E e, Object value, E... candidates) {
        for (E candidate : candidates) {
            if (e == candidate) {
                return e;
            }
        }
        throw new IllegalStateException(StrUtil.format("不支持的枚举[{}]值: {}", enumType.getSimpleName(), value));
    }


    /**
     * 判断枚举值是否存在
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> boolean exists(Class<E> enumType, V value) {
        if (Objects.isNull(value)) {
            return false;
        }
        return Objects.nonNull(getByValue(enumType, value));
    }


    /**
     * 获取枚举的 valueMap
     *
     * @param enumType 枚举类型
     * @return <字典值, 字典描述>
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> Map<V, String> getValueMap(Class<E> enumType) {
        E[] values = enumType.getEnumConstants();
        Map<V, String> valueMap = Maps.newHashMapWithExpectedSize(values.length);
        for (E value : values) {
            valueMap.put(value.getValue(), value.getDesc());
        }
        return Collections.unmodifiableMap(valueMap);
    }

    /**
     * 获取枚举的 DescMap
     *
     * @param enumType 枚举类型
     * @return <字典值, 字典描述>
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> Map<String, V> getDescMap(Class<E> enumType) {
        E[] values = enumType.getEnumConstants();
        Map<String, V> valueMap = Maps.newHashMapWithExpectedSize(values.length);
        for (E value : values) {
            valueMap.put(value.getDesc(), value.getValue());
        }
        return Collections.unmodifiableMap(valueMap);
    }


    /**
     * 获取枚举的 valueMap（会进行弱缓存）
     *
     * @param enumType 枚举类型
     * @return <字典值, 字典描述>
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> Map<V, String> getValueMapCache(Class<E> enumType) {
        return CacheUtils.weak().get(enumType.getName(), () -> getValueMap(enumType));
    }

    /**
     * 获取枚举的 valueMap（会进行弱缓存）
     *
     * @param enumType 枚举类型
     * @return <字典值, 字典描述>
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> Map<String, V> getKeyMapCache(Class<E> enumType) {
        return CacheUtils.weak().get(enumType.getName() + "desc", () -> getDescMap(enumType));
    }

    /**
     * 获取枚举值描述
     *
     * @param enumType     枚举类型
     * @param value        枚举值
     * @param defaultValue 默认值
     * @return 枚举描述
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> String getEnumDesc(Class<E> enumType, V value, String defaultValue) {
        if (Objects.isNull(value)) {
            return defaultValue;
        }
        return getValueMapCache(enumType).getOrDefault(value, defaultValue);
    }

    /**
     * 获取枚举值值
     *
     * @param enumType     枚举类型
     * @param desc         枚举描述
     * @param defaultValue 默认值
     * @return 枚举描述
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> V getEnumValue(Class<E> enumType, String desc, V defaultValue) {
        if (StrUtil.isBlank(desc)) {
            return defaultValue;
        }
        return getKeyMapCache(enumType).getOrDefault(desc, defaultValue);
    }

    /**
     * 获取枚举值描述
     *
     * @param enumType 枚举类型
     * @param value    枚举值
     * @return 枚举描述
     */
    @Nullable
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> String getEnumDesc(Class<E> enumType, V value) {
        return getEnumDesc(enumType, value, null);
    }


    /**
     * 获取枚举值描述
     *
     * @param enumType 枚举类型
     * @param value    枚举值
     * @return 枚举描述（不存在则返回原 value）
     */
    public static <E extends Enum<E> & EnumValueResolver<E, V>, V> String getEnumValueDesc(Class<E> enumType, V value) {
        String desc = getEnumDesc(enumType, value);
        if (Objects.nonNull(desc)) {
            return desc;
        }
        return FunctionUtils.map(value, String::valueOf);
    }


    /**
     * 判断目标值是否是枚举值
     */
    @SafeVarargs
    public static <V> boolean isIn(V value, EnumValueResolver<?, V>... enums) {
        Assert.notEmpty(enums);
        return isIn(value, CollUtil.newHashSet(enums));
    }


    /**
     * 判断目标值是否是枚举值
     */
    public static <V> boolean isIn(V value, Set<? extends EnumValueResolver<?, V>> enumSet) {
        Assert.notEmpty(enumSet);
        if (Objects.isNull(value)) {
            return false;
        }

        for (EnumValueResolver<?, V> anEnum : enumSet) {
            if (anEnum.isEquals(value)) {
                return true;
            }
        }
        return false;
    }


}
